use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::types::BasicTypeEnum;
use inkwell::values::BasicValueEnum;
use inkwell::AddressSpace;

impl<'ctx> LLVMBackend<'ctx> {


    /// Extract tag from Option value
    pub(in crate::llvm_backend) fn extract_option_tag(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
    ) -> Result<inkwell::values::IntValue<'ctx>> {
        if let BasicValueEnum::StructValue(struct_val) = option_value {
            let tag_value = self
                .builder
                .build_extract_value(struct_val, 0, "option_tag")?;
            Ok(tag_value.into_int_value())
        } else {
            Err(CompilerError::internal("Option value must be a struct"))
        }
    }


    /// Extract Some value from Option
    pub(in crate::llvm_backend) fn extract_option_some_value(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
    ) -> Result<BasicValueEnum<'ctx>> {
        let option_llvm_type = match self.get_option_llvm_type(option_type)? {
            BasicTypeEnum::StructType(st) => st,
            _ => return Err(CompilerError::internal("Option type must be a struct")),
        };
        let option_ptr = self.builder.build_alloca(option_llvm_type, "option_temp")?;
        self.builder.build_store(option_ptr, option_value)?;

        // Get pointer to data field (array pointer)
        let data_array_ptr = unsafe {
            self.builder.build_gep(
                option_llvm_type,
                option_ptr,
                &[
                    self.context.i32_type().const_zero(),
                    self.context.i32_type().const_int(1, false),
                ],
                "option_data_array_ptr",
            )?
        };

        // CRITICAL: GEP to first element of array to get i8*
        let data_first_byte_ptr = unsafe {
            self.builder.build_gep(
                self.context.i8_type().array_type(8), // Use fixed size for now
                data_array_ptr,
                &[
                    self.context.i32_type().const_zero(),
                    self.context.i32_type().const_zero(),
                ],
                "data_first_byte",
            )?
        };

        // Get Some type and cast i8* to target type pointer
        let some_llvm_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let some_ptr_type = match some_llvm_type {
            BasicTypeEnum::IntType(t) => t.ptr_type(AddressSpace::default()),
            BasicTypeEnum::FloatType(t) => t.ptr_type(AddressSpace::default()),
            BasicTypeEnum::PointerType(t) => t,
            BasicTypeEnum::StructType(t) => t.ptr_type(AddressSpace::default()),
            BasicTypeEnum::ArrayType(t) => t.ptr_type(AddressSpace::default()),
            BasicTypeEnum::VectorType(t) => t.ptr_type(AddressSpace::default()),
        };
        let some_ptr =
            self.builder
                .build_pointer_cast(data_first_byte_ptr, some_ptr_type, "some_ptr")?;

        // Load the Some value
        let some_value = self
            .builder
            .build_load(some_llvm_type, some_ptr, "some_value")?;
        Ok(some_value)
    }

}
